package cn.middle.remote.exchange;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

import cn.middle.remote.serialization.Serialization;
import cn.middle.remote.serialization.TypeGenericity;

public class DefaultFuture extends AbstractSignalFuture {
	
	private boolean operatorSuccess = false;
	
	private Serialization serializable;
	
	private byte[] data;
	
	protected Throwable cause;
	
    private volatile ReentrantLock lock = new ReentrantLock();
	
	private volatile Condition condition =  lock.newCondition();

	public DefaultFuture(String msgId) {
		super(msgId);
	}
	
	@Override
	public boolean isDone() {
		return data != null ;
	}
	
	@Override
	public <T> T get(TypeGenericity<T> clz) throws Throwable {
		return get(clz, DEFAULT_TIME_OUT);
	}

	@Override
	public <T> T get(TypeGenericity<T> clz, int waitTimeout) throws Throwable {
		if(waitTimeout <= 0) {
			waitTimeout = DEFAULT_TIME_OUT;
		}
		
		if(!isDone()) {
			lock.lock();
			try {
				while(!isDone()) {
					this.condition.await(waitTimeout,TimeUnit.MILLISECONDS);
					
					
					//已抛出异常，退出
					if(cause != null) {
						break;
					}
					//已完成
					if(isDone()) {
						break;
					}
					
					if((System.currentTimeMillis() - this.generatedstamp) > waitTimeout) {//已超时，抛出异常
						throw new TimeoutException("调用超时"); 
					}
				}
			}catch(Exception e) {
				cause(e);
			}finally {
				lock.unlock();
			}
		}
		
		if(this.cause != null) {
			throw cause;
		}
		
		return serializable.decode(data, clz);
	}
	

	@Override
	public boolean isSuccess() {
		return operatorSuccess;
	}
	
	public void setSerializable(Serialization serializable) {
		this.serializable = serializable;
	}


	@Override
	public void doResponse(byte[] data) {
		this.data = data;
		this.operatorSuccess = true;
		release();
	}

	@Override
	public void cause(Throwable cause) {
		this.cause = cause;
		release();
	}
	
	/**
	 *释放锁
	 */
	private void release() {
		lock.lock();
		try {
			if(condition != null) {
				condition.signal();
			}
		}finally {
			lock.unlock();
		}
	}

}
